home *** CD-ROM | disk | FTP | other *** search
/ Visual Cafe 3 / Visual Cafe 3.ISO / Vcafe / Main.bin / MemoryImageSource.java < prev    next >
Text File  |  1998-09-22  |  16KB  |  480 lines

  1. /*
  2.  * @(#)MemoryImageSource.java    1.19 98/07/01
  3.  *
  4.  * Copyright 1995-1998 by Sun Microsystems, Inc.,
  5.  * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
  6.  * All rights reserved.
  7.  * 
  8.  * This software is the confidential and proprietary information
  9.  * of Sun Microsystems, Inc. ("Confidential Information").  You
  10.  * shall not disclose such Confidential Information and shall use
  11.  * it only in accordance with the terms of the license agreement
  12.  * you entered into with Sun.
  13.  */
  14.  
  15. package java.awt.image;
  16.  
  17. import java.awt.image.ImageConsumer;
  18. import java.awt.image.ImageProducer;
  19. import java.awt.image.ColorModel;
  20. import java.util.Hashtable;
  21. import java.util.Vector;
  22. import java.util.Enumeration;
  23.  
  24. /**
  25.  * This class is an implementation of the ImageProducer interface which
  26.  * uses an array to produce pixel values for an Image.  Here is an example
  27.  * which calculates a 100x100 image representing a fade from black to blue
  28.  * along the X axis and a fade from black to red along the Y axis:
  29.  * <pre>
  30.  * 
  31.  *    int w = 100;
  32.  *    int h = 100;
  33.  *    int pix[] = new int[w * h];
  34.  *    int index = 0;
  35.  *    for (int y = 0; y < h; y++) {
  36.  *        int red = (y * 255) / (h - 1);
  37.  *        for (int x = 0; x < w; x++) {
  38.  *        int blue = (x * 255) / (w - 1);
  39.  *        pix[index++] = (255 << 24) | (red << 16) | blue;
  40.  *        }
  41.  *    }
  42.  *    Image img = createImage(new MemoryImageSource(w, h, pix, 0, w));
  43.  * 
  44.  * </pre>
  45.  * The MemoryImageSource is also capable of managing a memory image which
  46.  * varies over time to allow animation or custom rendering.  Here is an
  47.  * example showing how to set up the animation source and signal changes
  48.  * in the data (adapted from the MemoryAnimationSourceDemo by Garth Dickie):
  49.  * <pre>
  50.  *
  51.  *    int pixels[];
  52.  *    MemoryImageSource source;
  53.  *
  54.  *    public void init() {
  55.  *        int width = 50;
  56.  *        int height = 50;
  57.  *        int size = width * height;
  58.  *        pixels = new int[size];
  59.  *
  60.  *        int value = getBackground().getRGB();
  61.  *        for (int i = 0; i < size; i++) {
  62.  *        pixels[i] = value;
  63.  *        }
  64.  *
  65.  *        source = new MemoryImageSource(width, height, pixels, 0, width);
  66.  *        source.setAnimated(true);
  67.  *        image = createImage(source);
  68.  *    }
  69.  *
  70.  *    public void run() {
  71.  *        Thread me = Thread.currentThread( );
  72.  *        me.setPriority(Thread.MIN_PRIORITY);
  73.  *
  74.  *        while (true) {
  75.  *        try {
  76.  *            thread.sleep(10);
  77.  *        } catch( InterruptedException e ) {
  78.  *            return;
  79.  *        }
  80.  *
  81.  *        // Modify the values in the pixels array at (x, y, w, h)
  82.  *
  83.  *        // Send the new data to the interested ImageConsumers
  84.  *        source.newPixels(x, y, w, h);
  85.  *        }
  86.  *    }
  87.  *
  88.  * </pre>
  89.  *
  90.  * @see ImageProducer
  91.  *
  92.  * @version    1.19 07/01/98
  93.  * @author     Jim Graham
  94.  * @author    Animation capabilities inspired by the
  95.  *        MemoryAnimationSource class written by Garth Dickie
  96.  */
  97. public class MemoryImageSource implements ImageProducer {
  98.     int width;
  99.     int height;
  100.     ColorModel model;
  101.     Object pixels;
  102.     int pixeloffset;
  103.     int pixelscan;
  104.     Hashtable properties;
  105.     Vector theConsumers = new Vector();
  106.     boolean animating;
  107.     boolean fullbuffers;
  108.  
  109.     /**
  110.      * Constructs an ImageProducer object which uses an array of bytes
  111.      * to produce data for an Image object.
  112.      * @see java.awt.Component#createImage
  113.      */
  114.     public MemoryImageSource(int w, int h, ColorModel cm,
  115.                  byte[] pix, int off, int scan) {
  116.     initialize(w, h, cm, (Object) pix, off, scan, null);
  117.     }
  118.  
  119.     /**
  120.      * Constructs an ImageProducer object which uses an array of bytes
  121.      * to produce data for an Image object.
  122.      * @see java.awt.Component#createImage
  123.      */
  124.     public MemoryImageSource(int w, int h, ColorModel cm,
  125.                  byte[] pix, int off, int scan, Hashtable props) {
  126.     initialize(w, h, cm, (Object) pix, off, scan, props);
  127.     }
  128.  
  129.     /**
  130.      * Constructs an ImageProducer object which uses an array of integers
  131.      * to produce data for an Image object.
  132.      * @see java.awt.Component#createImage
  133.      */
  134.     public MemoryImageSource(int w, int h, ColorModel cm,
  135.                  int[] pix, int off, int scan) {
  136.     initialize(w, h, cm, (Object) pix, off, scan, null);
  137.     }
  138.  
  139.     /**
  140.      * Constructs an ImageProducer object which uses an array of integers
  141.      * to produce data for an Image object.
  142.      * @see java.awt.Component#createImage
  143.      */
  144.     public MemoryImageSource(int w, int h, ColorModel cm,
  145.                  int[] pix, int off, int scan, Hashtable props) {
  146.     initialize(w, h, cm, (Object) pix, off, scan, props);
  147.     }
  148.  
  149.     private void initialize(int w, int h, ColorModel cm,
  150.                 Object pix, int off, int scan, Hashtable props) {
  151.     width = w;
  152.     height = h;
  153.     model = cm;
  154.     pixels = pix;
  155.     pixeloffset = off;
  156.     pixelscan = scan;
  157.     if (props == null) {
  158.         props = new Hashtable();
  159.     }
  160.     properties = props;
  161.     }
  162.  
  163.     /**
  164.      * Constructs an ImageProducer object which uses an array of integers
  165.      * in the default RGB ColorModel to produce data for an Image object.
  166.      * @see java.awt.Component#createImage
  167.      * @see ColorModel#getRGBdefault
  168.      */
  169.     public MemoryImageSource(int w, int h, int pix[], int off, int scan) {
  170.     initialize(w, h, ColorModel.getRGBdefault(),
  171.            (Object) pix, off, scan, null);
  172.     }
  173.  
  174.     /**
  175.      * Constructs an ImageProducer object which uses an array of integers
  176.      * in the default RGB ColorModel to produce data for an Image object.
  177.      * @see java.awt.Component#createImage
  178.      * @see ColorModel#getRGBdefault
  179.      */
  180.     public MemoryImageSource(int w, int h, int pix[], int off, int scan,
  181.                  Hashtable props) {
  182.     initialize(w, h, ColorModel.getRGBdefault(),
  183.            (Object) pix, off, scan, props);
  184.     }
  185.  
  186.     /**
  187.      * Adds an ImageConsumer to the list of consumers interested in
  188.      * data for this image.
  189.      * @see ImageConsumer
  190.      */
  191.     public synchronized void addConsumer(ImageConsumer ic) {
  192.     if (theConsumers.contains(ic)) {
  193.         return;
  194.     }
  195.     theConsumers.addElement(ic);
  196.     try {
  197.         initConsumer(ic);
  198.         sendPixels(ic, 0, 0, width, height);
  199.         if (isConsumer(ic)) {
  200.         ic.imageComplete(animating
  201.                  ? ImageConsumer.SINGLEFRAMEDONE
  202.                  : ImageConsumer.STATICIMAGEDONE);
  203.         if (!animating && isConsumer(ic)) {
  204.             ic.imageComplete(ImageConsumer.IMAGEERROR);
  205.             removeConsumer(ic);
  206.         }
  207.         }
  208.     } catch (Exception e) {
  209.         if (isConsumer(ic)) {
  210.         ic.imageComplete(ImageConsumer.IMAGEERROR);
  211.         }
  212.     }
  213.     }
  214.  
  215.     /**
  216.      * Determine if an ImageConsumer is on the list of consumers currently
  217.      * interested in data for this image.
  218.      * @return true if the ImageConsumer is on the list; false otherwise
  219.      * @see ImageConsumer
  220.      */
  221.     public synchronized boolean isConsumer(ImageConsumer ic) {
  222.     return theConsumers.contains(ic);
  223.     }
  224.  
  225.     /**
  226.      * Remove an ImageConsumer from the list of consumers interested in
  227.      * data for this image.
  228.      * @see ImageConsumer
  229.      */
  230.     public synchronized void removeConsumer(ImageConsumer ic) {
  231.     theConsumers.removeElement(ic);
  232.     }
  233.  
  234.     /**
  235.      * Adds an ImageConsumer to the list of consumers interested in
  236.      * data for this image, and immediately start delivery of the
  237.      * image data through the ImageConsumer interface.
  238.      * @see ImageConsumer
  239.      */
  240.     public void startProduction(ImageConsumer ic) {
  241.     addConsumer(ic);
  242.     }
  243.  
  244.     /**
  245.      * Requests that a given ImageConsumer have the image data delivered
  246.      * one more time in top-down, left-right order.
  247.      * @see ImageConsumer
  248.      */
  249.     public void requestTopDownLeftRightResend(ImageConsumer ic) {
  250.     // Ignored.  The data is either single frame and already in TDLR
  251.     // format or it is multi-frame and TDLR resends aren't critical.
  252.     }
  253.  
  254.     /**
  255.      * Change this memory image into a multi-frame animation or a
  256.      * single-frame static image depending on the animated parameter.
  257.      * <p>This method should be called immediately after the
  258.      * MemoryImageSource is constructed and before an image is
  259.      * created with it to ensure that all ImageConsumers will
  260.      * receive the correct multi-frame data.  If an ImageConsumer
  261.      * is added to this ImageProducer before this flag is set then
  262.      * that ImageConsumer will see only a snapshot of the pixel
  263.      * data that was available when it connected.
  264.      * @param animated true if the image is a multi-frame animation
  265.      */
  266.     public synchronized void setAnimated(boolean animated) {
  267.     this.animating = animated;
  268.     if (!animating) {
  269.         Enumeration enum = theConsumers.elements();
  270.         while (enum.hasMoreElements()) {
  271.             ImageConsumer ic = (ImageConsumer) enum.nextElement();
  272.         ic.imageComplete(ImageConsumer.STATICIMAGEDONE);
  273.         if (isConsumer(ic)) {
  274.             ic.imageComplete(ImageConsumer.IMAGEERROR);
  275.         }
  276.         }
  277.         theConsumers.removeAllElements();
  278.     }
  279.     }
  280.  
  281.     /**
  282.      * Specify whether this animated memory image should always be
  283.      * updated by sending the complete buffer of pixels whenever
  284.      * there is a change.
  285.      * This flag is ignored if the animation flag is not turned on
  286.      * through the setAnimated() method.
  287.      * <p>This method should be called immediately after the
  288.      * MemoryImageSource is constructed and before an image is
  289.      * created with it to ensure that all ImageConsumers will
  290.      * receive the correct pixel delivery hints.
  291.      * @param fullbuffers true if the complete pixel buffer should always
  292.      * be sent
  293.      * @see #setAnimated
  294.      */
  295.     public synchronized void setFullBufferUpdates(boolean fullbuffers) {
  296.     if (this.fullbuffers == fullbuffers) {
  297.         return;
  298.     }
  299.     this.fullbuffers = fullbuffers;
  300.     if (animating) {
  301.         Enumeration enum = theConsumers.elements();
  302.         while (enum.hasMoreElements()) {
  303.             ImageConsumer ic = (ImageConsumer) enum.nextElement();
  304.         ic.setHints(fullbuffers
  305.                 ? (ImageConsumer.TOPDOWNLEFTRIGHT |
  306.                    ImageConsumer.COMPLETESCANLINES)
  307.                 : ImageConsumer.RANDOMPIXELORDER);
  308.         }
  309.     }
  310.     }
  311.  
  312.     /**
  313.      * Send a whole new buffer of pixels to any ImageConsumers that
  314.      * are currently interested in the data for this image and notify
  315.      * them that an animation frame is complete.
  316.      * This method only has effect if the animation flag has been
  317.      * turned on through the setAnimated() method.
  318.      * @see ImageConsumer
  319.      * @see #setAnimated
  320.      */
  321.     public void newPixels() {
  322.     newPixels(0, 0, width, height, true);
  323.     }
  324.  
  325.     /**
  326.      * Send a rectangular region of the buffer of pixels to any
  327.      * ImageConsumers that are currently interested in the data for
  328.      * this image and notify them that an animation frame is complete.
  329.      * This method only has effect if the animation flag has been
  330.      * turned on through the setAnimated() method.
  331.      * If the full buffer update flag was turned on with the
  332.      * setFullBufferUpdates() method then the rectangle parameters
  333.      * will be ignored and the entire buffer will always be sent.
  334.      * @param x the x coordinate of the upper left corner of the rectangle
  335.      * of pixels to be sent
  336.      * @param y the y coordinate of the upper left corner of the rectangle
  337.      * of pixels to be sent
  338.      * @param w the width of the rectangle of pixels to be sent
  339.      * @param h the height of the rectangle of pixels to be sent
  340.      * @see ImageConsumer
  341.      * @see #setAnimated
  342.      * @see #setFullBufferUpdates
  343.      */
  344.     public synchronized void newPixels(int x, int y, int w, int h) {
  345.     newPixels(x, y, w, h, true);
  346.     }
  347.  
  348.     /**
  349.      * Send a rectangular region of the buffer of pixels to any
  350.      * ImageConsumers that are currently interested in the data for
  351.      * this image.
  352.      * If the framenotify parameter is true then the consumers are
  353.      * also notified that an animation frame is complete.
  354.      * This method only has effect if the animation flag has been
  355.      * turned on through the setAnimated() method.
  356.      * If the full buffer update flag was turned on with the
  357.      * setFullBufferUpdates() method then the rectangle parameters
  358.      * will be ignored and the entire buffer will always be sent.
  359.      * @param x the x coordinate of the upper left corner of the rectangle
  360.      * of pixels to be sent
  361.      * @param y the y coordinate of the upper left corner of the rectangle
  362.      * of pixels to be sent
  363.      * @param w the width of the rectangle of pixels to be sent
  364.      * @param h the height of the rectangle of pixels to be sent
  365.      * @param framenotify true if the consumers should be sent a
  366.      * SINGLEFRAMEDONE notification
  367.      * @see ImageConsumer
  368.      * @see #setAnimated
  369.      * @see #setFullBufferUpdates
  370.      */
  371.     public synchronized void newPixels(int x, int y, int w, int h,
  372.                        boolean framenotify) {
  373.     if (animating) {
  374.         if (fullbuffers) {
  375.         x = y = 0;
  376.         w = width;
  377.         h = height;
  378.         } else {
  379.         if (x < 0) {
  380.             w += x;
  381.             x = 0;
  382.         }
  383.         if (x + w > width) {
  384.             w = width - x;
  385.         }
  386.         if (y < 0) {
  387.             h += y;
  388.             y = 0;
  389.         }
  390.         if (y + h > height) {
  391.             h = height - y;
  392.         }
  393.         }
  394.         if ((w <= 0 || h <= 0) && !framenotify) {
  395.         return;
  396.         }
  397.         Enumeration enum = theConsumers.elements();
  398.         while (enum.hasMoreElements()) {
  399.             ImageConsumer ic = (ImageConsumer) enum.nextElement();
  400.         if (w > 0 && h > 0) {
  401.             sendPixels(ic, x, y, w, h);
  402.         }
  403.         if (framenotify && isConsumer(ic)) {
  404.             ic.imageComplete(ImageConsumer.SINGLEFRAMEDONE);
  405.         }
  406.         }
  407.     }
  408.     }
  409.  
  410.     /**
  411.      * Change to a new byte array to hold the pixels for this image.
  412.      * If the animation flag has been turned on through the setAnimated()
  413.      * method, then the new pixels will be immediately delivered to any
  414.      * ImageConsumers that are currently interested in the data for
  415.      * this image.
  416.      * @see #setAnimated
  417.      */
  418.     public synchronized void newPixels(byte[] newpix, ColorModel newmodel,
  419.                        int offset, int scansize) {
  420.     this.pixels = newpix;
  421.     this.model = newmodel;
  422.     this.pixeloffset = offset;
  423.     this.pixelscan = scansize;
  424.     newPixels();
  425.     }
  426.  
  427.     /**
  428.      * Change to a new int array to hold the pixels for this image.
  429.      * If the animation flag has been turned on through the setAnimated()
  430.      * method, then the new pixels will be immediately delivered to any
  431.      * ImageConsumers that are currently interested in the data for
  432.      * this image.
  433.      * @see #setAnimated
  434.      */
  435.     public synchronized void newPixels(int[] newpix, ColorModel newmodel,
  436.                        int offset, int scansize) {
  437.     this.pixels = newpix;
  438.     this.model = newmodel;
  439.     this.pixeloffset = offset;
  440.     this.pixelscan = scansize;
  441.     newPixels();
  442.     }
  443.  
  444.     private void initConsumer(ImageConsumer ic) {
  445.     if (isConsumer(ic)) {
  446.         ic.setDimensions(width, height);
  447.     }
  448.     if (isConsumer(ic)) {
  449.         ic.setProperties(properties);
  450.     }
  451.     if (isConsumer(ic)) {
  452.         ic.setColorModel(model);
  453.     }
  454.     if (isConsumer(ic)) {
  455.         ic.setHints(animating
  456.             ? (fullbuffers
  457.                ? (ImageConsumer.TOPDOWNLEFTRIGHT |
  458.                   ImageConsumer.COMPLETESCANLINES)
  459.                : ImageConsumer.RANDOMPIXELORDER)
  460.             : (ImageConsumer.TOPDOWNLEFTRIGHT |
  461.                ImageConsumer.COMPLETESCANLINES |
  462.                ImageConsumer.SINGLEPASS |
  463.                ImageConsumer.SINGLEFRAME));
  464.     }
  465.     }
  466.  
  467.     private void sendPixels(ImageConsumer ic, int x, int y, int w, int h) {
  468.     int off = pixeloffset + pixelscan * y + x;
  469.     if (isConsumer(ic)) {
  470.         if (pixels instanceof byte[]) {
  471.         ic.setPixels(x, y, w, h, model,
  472.                  ((byte[]) pixels), off, pixelscan);
  473.         } else {
  474.         ic.setPixels(x, y, w, h, model,
  475.                  ((int[]) pixels), off, pixelscan);
  476.         }
  477.     }
  478.     }
  479. }
  480.